package org.estela;


import java.util.ArrayList;
import java.util.List;
import java.util.Map;

import org.estela.util.SmallMap;

/**
 * 
 * <code>
 * 	public static void main(String[] args) {
 * 		CommandLine cmdLine = new CommandLine();
 * 		cmdLine.addOption("hertz", CommandLine.REQUIRED_ARGUMENT, 'h');
 * 		cmdLine.addOption("system", CommandLine.REQUIRED_ARGUMENT, 's');
 * 
 * 		//cmdLine.parse(new String[]{"-h","50", "--system", "PAL"}); OK
 * 		cmdLine.parse(new String[]{"-h","50", "-s", "PAL"});
 * 
 * 		while (cmdLine.hasMoreOptions()) {
 * 			cmdLine.nextOption();
 * 			char option = cmdLine.getOption();
 * 			switch (option) {
 * 				case 'h':
 * 					System.out.println(cmdLine.getArgument());
 * 					break;
 * 
 * 				case 's':
 * 					System.out.println(cmdLine.getArgument());
 * 					break;
 * 			}
 * 		}
 * 	}
 * 
 * </code>
 * @since 1.00
 * @version $Revision$
 */
public class ArgsParser {

	public static final int NO_ARGUMENT = 0;
	
	public static final int REQUIRED_ARGUMENT = 1;

	private int idx = -1;
	
	private Map<String,Option> optionMap = new SmallMap<String,Option>();
	
	private Map<Character,Option> parsedMap = new SmallMap<Character,Option>();

	private List<Option> options = new ArrayList<Option>();

	/**
	 * 
	 * @param args
	 */
	public ArgsParser() {
	}
	
	/**
	 * 
	 * @param cmd
	 * @param arg
	 * @param c
	 */
	public void addOption(String cmd, int argOpt, char c) {
		Option option = new Option(cmd, argOpt, c);
		this.optionMap.put("-" + c, option);
		this.optionMap.put("--" + cmd, option);
	}
	
	/**
	 * 
	 * @param args
	 */
	public void parse(String[] args) {
		for (int i = 0; i < args.length; i++) {
			Option option = this.optionMap.get(args[i]);
			if (option == null) {
				throw new Error("Invalid option: " + args[i]);
			}
			if (option.argOpt == REQUIRED_ARGUMENT) {
				option.arg = args[++i];
				this.parsedMap.put(option.c, option);
			}
			this.options.add(option);
		}
	}

	/**
	 * 
	 * @return
	 */
	public boolean hasMoreOptions() {
		return this.idx < options.size()-1;
	}
	
	/**
	 * 
	 *
	 */
	public void nextOption() {
		this.idx++;
	}	

	/**
	 * 
	 * @return
	 */
	public char getOption() {
		Option option = this.options.get(this.idx);
		return option.c;
	}

	/**
	 * 
	 * @return
	 */
	public String getArgument() {
		Option option = this.options.get(this.idx);
		return option.arg;
	}
	
	public static void main(String[] args) {
		ArgsParser cmdLine = new ArgsParser();
		cmdLine.addOption("hertz", ArgsParser.REQUIRED_ARGUMENT, 'h');
		cmdLine.addOption("system", ArgsParser.REQUIRED_ARGUMENT, 's');
		
		//cmdLine.parse(new String[]{"-h","50", "--system", "PAL"}); OK
		cmdLine.parse(new String[]{"-h","50", "-s", "PAL"});
		
		while (cmdLine.hasMoreOptions()) {
			cmdLine.nextOption();
			char option = cmdLine.getOption();
			switch (option) {
				case 'h':
					System.out.println(cmdLine.getArgument());
					break;
					
				case 's':
					System.out.println(cmdLine.getArgument());
					break;
					
			}
		}
	}

	/**
	 * 
	 * @date Mar 22, 2006
	 * @version $Revision: 1.1 $
	 * @author <a href="mailto:akantek@nyse.com">Antonio Kantek</a>
	 */
	public class Option {
		char c;
		
		String arg;
		
		String cmd;
		
		int argOpt;
		
		@Override
		public int hashCode() {
			return Character.valueOf(c).hashCode();
		}
		
		@Override
		public boolean equals(Object obj) {
			Option other = (Option) obj;
			return other.c == this.c;
		}
		
		/**
		 * 
		 * @param cmd
		 * @param argOpt
		 * @param c
		 */
		public Option(String cmd, int argOpt, char c) {
			this.c = c;
			this.cmd = cmd;
			this.argOpt = argOpt;
		}
	}

	public String getArgument(char c) {
		return parsedMap.get(Character.valueOf(c)).arg;
	}
}